﻿using HARTCalibrationTool.SerialPortManager;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace HARTCalibrationTool.Commands.HART
{
    public static class HARTObserversInfo
    {
        public static Dictionary<string, string> info = new Dictionary<string, string>()
        {
            // #0
            { "HART #0 All", "HART CMD0 All"},
            { "HART #0 chl1 LongAddress", "HART CMD0 Channel1 LongAddress"},
            { "HART #0 chl2 LongAddress", "HART CMD0 Channel2 LongAddress"},
            { "HART #0 chl3 LongAddress", "HART CMD0 Channel3 LongAddress"},
            { "HART #0 chl4 LongAddress", "HART CMD0 Channel4 LongAddress"},
            { "HART #0 chl5 LongAddress", "HART CMD0 Channel5 LongAddress"},
            { "HART #0 chl6 LongAddress", "HART CMD0 Channel6 LongAddress"},
            { "HART #0 chl7 LongAddress", "HART CMD0 Channel7 LongAddress"},
            { "HART #0 chl8 LongAddress", "HART CMD0 Channel8 LongAddress"},
            { "HART #0 chl9 LongAddress", "HART CMD0 Channel9 LongAddress"},
            { "HART #0 chl10 LongAddress", "HART CMD0 Channel10 LongAddress"},
            { "HART #0 chl11 LongAddress", "HART CMD0 Channel11 LongAddress"},
            { "HART #0 chl12 LongAddress", "HART CMD0 Channel12 LongAddress"},
            { "HART #0 chl13 LongAddress", "HART CMD0 Channel13 LongAddress"},
            { "HART #0 chl14 LongAddress", "HART CMD0 Channel14 LongAddress"},
            { "HART #0 chl15 LongAddress", "HART CMD0 Channel15 LongAddress"},
            { "HART #0 chl16 LongAddress", "HART CMD0 Channel16 LongAddress"},
            { "HART #0 chl17 LongAddress", "HART CMD0 Channel17 LongAddress"},
            { "HART #0 chl18 LongAddress", "HART CMD0 Channel18 LongAddress"},
            { "HART #0 chl19 LongAddress", "HART CMD0 Channel19 LongAddress"},
            { "HART #0 chl20 LongAddress", "HART CMD0 Channel20 LongAddress"},
            { "HART #0 chl21 LongAddress", "HART CMD0 Channel21 LongAddress"},
            { "HART #0 chl22 LongAddress", "HART CMD0 Channel22 LongAddress"},
            { "HART #0 chl23 LongAddress", "HART CMD0 Channel23 LongAddress"},
            { "HART #0 chl24 LongAddress", "HART CMD0 Channel24 LongAddress"},
            { "HART #0 chl25 LongAddress", "HART CMD0 Channel25 LongAddress"},
            { "HART #0 chl26 LongAddress", "HART CMD0 Channel26 LongAddress"},
            { "HART #0 chl27 LongAddress", "HART CMD0 Channel27 LongAddress"},
            { "HART #0 chl28 LongAddress", "HART CMD0 Channel28 LongAddress"},
            { "HART #0 chl29 LongAddress", "HART CMD0 Channel29 LongAddress"},
            { "HART #0 chl30 LongAddress", "HART CMD0 Channel30 LongAddress"},
            { "HART #0 chl31 LongAddress", "HART CMD0 Channel31 LongAddress"},
            { "HART #0 chl32 LongAddress", "HART CMD0 Channel32 LongAddress"},

            // #1
            { "HART #1 Read PV", "HART #1 Read Primary Variable"},
            { "HART #1 Show PV", "HART #1 Show Primary Variable"},
            { "HART #2 Read Current", "HART #2 Read Current"},
            { "HART #2 Show Current", "HART #2 Show Current"},
            { "HART #1 Read PV Retest", "HART #1 Read Primary Variable Retest"},
            { "HART #2 Read Current Retest", "HART #2 Read Current Retest"},

            { "HART #1 Read PV Excel", "Retest HART #1 Read PV Excel"},
            { "HART #2 Read Current Excel", "Retest HART #2 Read Current Excel"},

            // #14
            { "HART #14 Upper", "HART #14 Upper Transducer Limit"},
            { "HART #14 Lower", "HART #14 Lower Transducer Limit"},

            // #15
            { "HART #15 PV Upper", "HART #15 PV Upper Range Value"},
            { "HART #15 PV Lower", "HART #15 PV Lower Range Value"},

            // #40
            { "HART #40 Enter Fixed Current Mode", "HART #40 Enter Fixed Current Mode"},
            { "HART #40 Exit Fixed Current Mode", "HART #40 Exit Fixed Current Mode"},

            // #135
            { "HART #135#03 Get Flgs", "HART #135 Get Cal Flgs"},
            { "HART #135#03 Get TPoints Flg", "HART #135 Get Temperature Points Cal Flg"},
            { "HART #135#86 Set Fac Param", "HART #135#86 Set Factory Paramters"},
        };
    }
    public abstract class HARTCMDsBase : CommandsBase
    {
        private static DeviceInfoStruct[] _deviceInfoStruct;
        protected HARTCMDsCodeStruct _stHARTCMDsStruct;

        public struct DeviceInfoStruct
        {
            public byte shortAddr;
            public byte deviceID1;
            public byte deviceID2;
            public byte deviceID3;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct HARTCMDsCodeStruct
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
            public byte[] preamble;
            public byte start;
            public byte addressByte1;
            public byte addressByte2;
            public byte addressByte3;
            public byte addressByte4;
            public byte addressByte5;
            public byte cmdID;
            public byte size;
            public byte parity;
        }

        // 静态构造函数可以确保静态字段（如数组）只会在类第一次使用时初始化一次。之后的所有实例化过程都不会再次初始化这个静态字段。
        static HARTCMDsBase()
        {
            _deviceInfoStruct = new DeviceInfoStruct[32];
        }

        public HARTCMDsBase(SerialPortsBase serialPortManager, string commandName) : base(serialPortManager, commandName) 
        {
            
        }

        public void SetShortAddress(byte addr)
        {
            byte chlX = (byte)(CurrentGroupChannel - 1);

            if ((addr >= 0x00) && (addr <= 0x0f))
            {
                _deviceInfoStruct[chlX].shortAddr = addr;
            }
        }

        public byte GetShortAddress()
        {
            byte chlX = (byte)(CurrentGroupChannel - 1);

            return _deviceInfoStruct[chlX].shortAddr;
        }

        public void SetDeviceID(byte deviceID1, byte deviceID2, byte deviceID3)
        {
            byte chlX = (byte)(CurrentGroupChannel - 1);

            if (chlX != 0xff)
            {
                _deviceInfoStruct[chlX].deviceID1 = deviceID1;
                _deviceInfoStruct[chlX].deviceID2 = deviceID2;
                _deviceInfoStruct[chlX].deviceID3 = deviceID3;
            }
        }

        public byte[] GetDeviceID()
        {
            byte[] bytes = new byte[3];
            byte chlX = (byte)(CurrentGroupChannel - 1);

            if (chlX != 0xff)
            {
                bytes[0] = _deviceInfoStruct[chlX].deviceID1;
                bytes[1] = _deviceInfoStruct[chlX].deviceID2;
                bytes[2] = _deviceInfoStruct[chlX].deviceID3;
            }
            else
            {
                bytes[0] = 0x00;
                bytes[1] = 0x00;
                bytes[2] = 0x00;
            }

            return bytes;
        }

        public override async Task ExecuteCMD()
        {
            await SendCMD();
        }

        public override async Task ParseResponse(byte[] data)
        {
            await Task.Run(() => { });
        }

        protected abstract Task SendCMD();

        protected byte[] StructToBytes<T>(T stData)
        { 
            int size = Marshal.SizeOf(stData);
            byte[] arr = new byte[size];

            IntPtr ptr = Marshal.AllocHGlobal(size);

            try
            {
                Marshal.StructureToPtr(stData, ptr, true);
                Marshal.Copy(ptr, arr, 0, size);
            }
            catch 
            {
#if DebugMessage
                SerialPortsBase.PrintMessage("byte[] StructToBytes<T>(T stData), failed!");
#endif
            }
            finally
            {
                Marshal.FreeHGlobal(ptr);
            }

            return arr;
        }

        public override VerifyDataFrameStatus VerifyDataFrameIntegrity(byte[] data)
        {
            byte ucCRC = 0;
            byte frameSize = 0;
            byte frameStartPos = 0;

            if ((data == null) || (data.Length == 0))
            {
                return VerifyDataFrameStatus.eError;
            }

            if (data.Length >= 8)
            {
                if ((data[0] == 0x06) || (data[0] == 0x86))
                {
                    frameStartPos = 0;
                }
                else if ((data[1] == 0x06) || (data[1] == 0x86))
                {
                    frameStartPos = 1;
                }
                else if ((data[2] == 0x06) || (data[2] == 0x86))
                {
                    frameStartPos = 2;
                }
                else if ((data[3] == 0x06) || (data[3] == 0x86))
                {
                    frameStartPos = 3;
                }
                else if ((data[4] == 0x06) || (data[4] == 0x86))
                {
                    frameStartPos = 4;
                }
                else if ((data[5] == 0x06) || (data[5] == 0x86))
                {
                    frameStartPos = 5;
                }
                else if ((data[6] == 0x06) || (data[6] == 0x86))
                {
                    frameStartPos = 6;
                }
                else if ((data[7] == 0x06) || (data[7] == 0x86))
                {
                    frameStartPos = 7;
                }
                else
                {
                    return VerifyDataFrameStatus.eError;
                }

                try
                {
                    switch (data[frameStartPos])
                    {
                        case 0x06:
                            frameSize = (byte)(4 + data[frameStartPos + 3] + 1);
                            break;
                        case 0x86:
                            frameSize = (byte)(8 + data[frameStartPos + 7] + 1);
                            break;
                        default:
                            break;
                    }
                }
                catch
                {
                    return VerifyDataFrameStatus.eWaiting;
                }


                if ((data.Length - frameStartPos) >= frameSize)
                {
                    byte[] frameData = new byte[frameSize];

                    Array.Copy(data, frameStartPos, frameData, 0, frameSize);

                    ucCRC = Utils.GetHARTCRC(frameData, (byte)(frameSize - 1));

                    if (ucCRC == frameData[frameSize - 1])
                    {
                        return VerifyDataFrameStatus.eSuccess;
                    }
                    else
                    {
                        return VerifyDataFrameStatus.eCRCError;
                    }
                }
                else
                {
                    return VerifyDataFrameStatus.eWaiting;
                }
            }
            else
            {
                return VerifyDataFrameStatus.eWaiting;
            }
        }
    }
}
